﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;

namespace MicroDb.Mysql
{
    class MysqlResolveNonQuery : MysqlResolveBase
    {

        private string MethodName;

        private object constantObj;

        private PrimaryAttribute Primary;
        private List<string> Wheres { get; set; }

        private List<string> Sets { get; set; }


        /// <summary>
        /// 单项值
        /// </summary>
        private bool IsScalar;


        public MysqlResolveNonQuery(MethodCallExpression callExpression, SqlExecute sqlExecute = null) : base(sqlExecute)
        { //base.TableType = "";


            MethodName = callExpression.Method.Name;
            switch (callExpression.Method.Name)
            {
                case "UpdateColumns":
                    AppendSet(callExpression.Arguments[1] as UnaryExpression);
                    break;
                case "Add":
                case "AddRange":
                case "AddOrUpdate":
                case "Update":
                    constantObj = (callExpression.Arguments[1] as ConstantExpression)?.Value;
                    break;
                case "Delete":
                    break;
            }
            Init(callExpression.Arguments[0]);
        }


        private void Init(Expression expression)
        {
            while (expression.NodeType == ExpressionType.Call)
            {
                var callExpression = expression as MethodCallExpression;
                switch (callExpression.Method.Name)
                {
                    case "Where":
                        if (Wheres == null)
                        {
                            Wheres = new List<string>();
                        }
                        Wheres.Add(WhereAny(callExpression.Arguments[1] as UnaryExpression));
                        break;
                    case "SuffixAutoCreate":
                        Suffix = GetValue(callExpression.Arguments[1]).ToString();
                        AutoCreate = true;
                        break;
                    case "Suffix":
                        Suffix = GetValue(callExpression.Arguments[1]).ToString();
                        break;
                    default:
                        throw new NotImplementedException(callExpression.Method.Name);
                }
                expression = callExpression.Arguments[0];
            }
            if (expression.NodeType != ExpressionType.Constant)
            {
                throw new ArgumentNullException("source");
            }
            base.TableType = (GetValue(expression) as IQueryable).ElementType;
        }


        private void AppendSet(UnaryExpression unaryExpression)
        {
            var lambda = unaryExpression.Operand as LambdaExpression;
            if (lambda.Body.NodeType != ExpressionType.MemberInit)
                throw new NotImplementedException("UpdateColumns type:" + lambda.Body.NodeType);

            var member = lambda.Body as MemberInitExpression;
            if (member.Bindings.Count == 0) throw new Exception("没有更新字段");
            Sets = new List<string>();
            foreach (MemberAssignment item in member.Bindings)
            {
                var name = ResolveAny(item.Expression, GetDefaultParameters(lambda));
                Sets.Add($"{CostName(item.Member.Name)}={name}");
            }
        }

        private string GetDelete()
        {
            return string.Format("DELETE FROM {0} {1}", GetTabName(), GetWhereSql());
        }

        private string GetWhereSql()
        {
            if (Wheres == null || Wheres.Count == 0)
            {
                return "";
            }
            return string.Concat("WHERE ", string.Join(" AND ", Wheres));
        }

        public bool NEquals(object obj, object obj2)
        {
            if (obj == null)
            {
                return obj2 == null;
            }
            if (obj2 != null)
            {
                return obj.GetHashCode() == obj2.GetHashCode();
            }
            return false;
        }

        private bool IsDefaultValue(object v, System.Data.DbType dbType)
        {
            switch (dbType)
            {
                case System.Data.DbType.Boolean:
                case System.Data.DbType.Byte:
                case System.Data.DbType.Int16:
                case System.Data.DbType.Int32:
                case System.Data.DbType.Int64:
                case System.Data.DbType.UInt16:
                case System.Data.DbType.UInt32:
                case System.Data.DbType.UInt64:
                    return 0 == v.GetHashCode();
                default:
                    return v == null;
            }
        }
        /// <summary>
        /// 自动主键
        /// </summary>
        /// <returns></returns>
        private void AddAuto(object obj)
        {//主键空 或者 0 添加

            if (!Primary.Automatic && IsDefaultValue(Primary.GetValue(obj), Primary.FieldType.Value))
            {//非递增主键 
                if (Primary.PrimaryType == PrimaryType.Guid)
                {
                    Primary.SetValue(obj, Guid.NewGuid().ToString("N"));
                }
                else if (Primary.PrimaryType == PrimaryType.TimeTicks)
                {
                    lock (this)
                    {
                        if (Primary.FieldType == System.Data.DbType.Int64)
                        {
                            Primary.SetValue(obj, long.Parse(DateTime.Now.Ticks.ToString()[3..]));
                        }
                        else
                        {
                            Primary.SetValue(obj, int.Parse(DateTime.Now.Ticks.ToString()[9..]));
                        }
                    }
                }
                else
                {
                    throw Error.SystemError("缺少主键:" + Primary.PropertyName);
                }
            }
        }

        /// <summary>
        /// 批量加
        /// </summary>
        /// <param name="sqlExecute"></param>
        /// <returns></returns>
        private string GetAddRangeSql(ColumnAttribute[] columns, SqlExecute sqlExecute)
        {

            if (constantObj == null)
            {
                throw new ArgumentNullException("" + TableType.Name);
            }
            var fieldarray = columns.Where(a => !a.Automatic).ToArray();
            StringBuilder sqlInser = new StringBuilder();
            foreach (var item in constantObj as IEnumerable)
            {
                if (Primary != null)
                    AddAuto(item);
                sqlInser.AppendFormat("({0}),", string.Join(",", RangeSql(fieldarray, sqlExecute, item)));
            }
            return $"INSERT INTO {GetTabName()} ({ string.Join(",", fieldarray.Select(a => a.Name).ToArray())}) VALUES " + sqlInser.ToString().TrimEnd(',');
        }

        private IEnumerable<string> RangeSql(ColumnAttribute[] fields, SqlExecute sqlExecute, object obj)
        {
            foreach (var field in fields)
            {
                var value = field.GetValue(obj);
                if (sqlExecute.InsertFilter(field.PropertyName, ref value))
                {
                    yield return AppendArg(field.PropertyName, value);
                }
            }
        }

        private string GetAddOrUpdateSql(ColumnAttribute[] fields, SqlExecute sqlExecute)
        {
            if (constantObj == null)
            {
                throw new ArgumentNullException("" + TableType.Name);
            }
            if (Primary == null) throw Error.SystemError($"表：{TableType.Name} 缺少主键");
            if (IsDefaultValue(Primary.GetValue(constantObj), Primary.FieldType.Value))
            {
                return GetAddSql(fields, sqlExecute);
            }
            return GetUpdateSql(fields, sqlExecute);
        }

        private string GetAddSql(ColumnAttribute[] ps, SqlExecute sqlExecute)
        {
            if (constantObj == null)
            {
                throw new ArgumentNullException("" + TableType.Name);
            }
            var InsertData = new Dictionary<string, string>();
            string sqlEnd = "";
            if (Primary != null)
            {
                AddAuto(constantObj);
                if (Primary.Automatic)
                {
                    IsScalar = true;
                    sqlEnd = "SELECT @@IDENTITY;";
                }
            }
            foreach (var item in ps.Where(a => !a.Automatic))
            {
                object value = item.GetValue(constantObj);
                if (sqlExecute.InsertFilter(item.PropertyName, ref value))
                {
                    InsertData.Add(item.Name, AppendArg(item.PropertyName, value));
                }
            }
            return $"INSERT INTO {GetTabName()} ({ string.Join(",", InsertData.Select(a => a.Key).ToArray())}) VALUES ({ string.Join(",", InsertData.Select(a => a.Value).ToArray())});" + sqlEnd;
        }


        private string GetUpdateSql(ColumnAttribute[] ps, SqlExecute sqlExecute)
        {
            if (constantObj == null)
            {
                throw new ArgumentNullException("" + TableType.Name);
            }
            var UpdateData = new Dictionary<string, string>();
            foreach (var item in ps.Where(a => !a.Automatic))
            {
                object value = item.GetValue(constantObj);
                if (sqlExecute.UpdateFilter(item.PropertyName, ref value))
                {
                    UpdateData.Add(item.Name, AppendArg(item.PropertyName, value));
                }
            }
            string where = GetWhereSql();
            if (string.IsNullOrEmpty(where))
            {
                if (Primary != null)
                {
                    where = $"WHERE {Primary.Name}={AppendArg(Primary.PropertyName, Primary.GetValue(constantObj))}";
                }
                else
                {
                    throw Error.SystemError("缺少主键:" + Primary.PropertyName);
                }
            }
            return $"UPDATE {GetTabName()} SET {string.Join(",", UpdateData.Select(a => string.Format("{0}={1} ", a.Key, a.Value)))} {where}";
        }



        public string BuildSql(SqlExecute sqlExecute)
        {
            var ps = GetColumns(TableType).ToArray();
            Primary = ps.FirstOrDefault(a => a is PrimaryAttribute) as PrimaryAttribute;

            switch (MethodName)
            {
                case "Delete":
                    return GetDelete();
                case "AddRange":
                    return GetAddRangeSql(ps, sqlExecute);
                case "Add":
                    return GetAddSql(ps, sqlExecute);
                case "Update":
                    return GetUpdateSql(ps, sqlExecute);
                case "AddOrUpdate":
                    return GetAddOrUpdateSql(ps, sqlExecute);
                case "UpdateColumns":
                    return $"UPDATE {GetTabName()} SET {string.Join(",", Sets)} {GetWhereSql()}";
            }
            throw new NotImplementedException("MethodName");
        }


        public override object GetResult()
        {
            string sql = BuildSql(sqlExecute);
            return TryCreateTable(() =>
            {
                if (IsScalar)
                {
                    object obj = sqlExecute.ExecuteScalar(System.Data.CommandType.Text, sql, Args);
                    if (Primary != null)
                    {
                        Primary.SetValue(constantObj, Convert.ChangeType(obj, Primary.PropertyType));
                    }
                    return true;
                }
                else
                {
                    return sqlExecute.ExecuteNonQuery(System.Data.CommandType.Text, sql, Args) > 0;
                }
            });
        }
    }

}
